/***************************************************************************
**  This file is part of Serial Port Plotter                              **
**                                                                        **
**                                                                        **
**  Serial Port Plotter is a program for plotting integer data from       **
**  serial port using Qt and QCustomPlot                                  **
**                                                                        **
**  This program is free software: you can redistribute it and/or modify  **
**  it under the terms of the GNU General Public License as published by  **
**  the Free Software Foundation, either version 3 of the License, or     **
**  (at your option) any later version.                                   **
**                                                                        **
**  This program is distributed in the hope that it will be useful,       **
**  but WITHOUT ANY WARRANTY; without even the implied warranty of        **
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         **
**  GNU General Public License for more details.                          **
**                                                                        **
**  You should have received a copy of the GNU General Public License     **
**  along with this program.  If not, see http://www.gnu.org/licenses/.   **
**                                                                        **
****************************************************************************
**           Author: Borislav                                             **
**           Contact: b.kereziev@gmail.com                                **
**           Date: 29.12.14                                               **
****************************************************************************/

#include "mainwindow.hpp"
#include "ui_mainwindow.h"
#include <x86intrin.h>

MainWindow::MainWindow (QWidget *parent) :
    QMainWindow (parent),
    ui (new Ui::MainWindow)
{
    ui->setupUi (this);

    createUI();

    setupPlot();

    serialPort = new QSerialPort(this);
    connect (serialPort, SIGNAL(readyRead()), this, SLOT(readData()));
    connect (this, SIGNAL(newData(QStringList)), this, SLOT(onNewDataArrived(QStringList)));
    connect (this, SIGNAL(newData(QStringList)), this, SLOT(saveStream(QStringList)));

    connect(ui->textBrowser, &QTextEdit::textChanged, this, [=](){
        ui->textBrowser->moveCursor(QTextCursor::End);
    });

    connect(ui->hideRecvAreaCheckBox, &QCheckBox::stateChanged, this, [=](int state){
        if(state == Qt::Checked)
        {
            ui->textBrowser->setVisible(false);
        }else
        {
            ui->textBrowser->setVisible(true);
        }
    });

//    connect(ui->hideRecvDataCheckBox, &QCheckBox::stateChanged, this, [=](int state){
//        if(state == Qt::Checked)
//        {
//            ui->hideRecvAreaCheckBox->setChecked(true);
//            ui->hideRecvAreaCheckBox->setVisible(false);
//            ui->dataFormatCheckBox->setVisible(false);
//        }else
//        {
//            ui->hideRecvAreaCheckBox->setChecked(false);
//            ui->hideRecvAreaCheckBox->setVisible(true);
//            ui->dataFormatCheckBox->setVisible(true);
//        }
//    });
}

MainWindow::~MainWindow()
{
    on_actionDisconnect_triggered();
    delete ui;
}

void MainWindow::closeEvent(QCloseEvent* e)
{
    Settings::setPortName(ui->comboPort->currentText());
    Settings::setBaudrate(ui->comboBaud->currentText().toInt());
    Settings::setDatabits(ui->comboData->currentIndex());
    Settings::setStopbits(ui->comboStop->currentIndex());
    Settings::setParity(ui->comboParity->currentIndex());
    Settings::setTheme(Settings::getTheme());
    Settings::setLineColors(Settings::getLineColors());
    Settings::setHideRecvArea(ui->hideRecvAreaCheckBox->isChecked());
    Settings::setHideRecvData(ui->hideRecvDataCheckBox->isChecked());
    Settings::setDisplayRawData(ui->dataFormatCheckBox->isChecked());
    Settings::setWindowPos(pos());
    Settings::setWindowSize(size());
    QList<int> l;
    l.append(ui->spinPoints->value());
    l.append(ui->spinYStep->value());
    l.append(ui->spinAxesMin->value());
    l.append(ui->spinAxesMax->value());
    Settings::setPlotArgs(l);
    QWidget::closeEvent(e);
}

void MainWindow::createUI()
{
    for (QSerialPortInfo port : QSerialPortInfo::availablePorts())
    {
        ui->comboPort->addItem (port.portName());
    }

    /* Populate baud rate combo box with standard rates */
    ui->comboBaud->addItem ("1200");
    ui->comboBaud->addItem ("2400");
    ui->comboBaud->addItem ("4800");
    ui->comboBaud->addItem ("9600");
    ui->comboBaud->addItem ("19200");
    ui->comboBaud->addItem ("38400");
    ui->comboBaud->addItem ("57600");
    ui->comboBaud->addItem ("115200");

    /* And some not-so-standard */
    ui->comboBaud->addItem ("128000");
    ui->comboBaud->addItem ("153600");
    ui->comboBaud->addItem ("230400");
    ui->comboBaud->addItem ("256000");
    ui->comboBaud->addItem ("460800");
    ui->comboBaud->addItem ("921600");

    /* Populate data bits combo box */
    ui->comboData->addItem (tr("8位"));
    ui->comboData->addItem (tr("7位"));

    /* Populate parity combo box */
    ui->comboParity->addItem (tr("无校验"));
    ui->comboParity->addItem (tr("奇校验"));
    ui->comboParity->addItem (tr("偶校验"));

    /* Populate stop bits combo box */
    ui->comboStop->addItem (tr("1位"));
    ui->comboStop->addItem (tr("2位"));

    /* Initialize the listwidget */
    ui->listWidget_Channels->clear();

    ui->comboPort->installEventFilter(this);

    ui->textBrowser->document()->setMaximumBlockCount(1000);  /* 设置最大显示1000行 */

    ui->comboPort->setCurrentIndex(ui->comboPort->findText(Settings::getPortName()));
    ui->comboBaud->setCurrentText(QString("%1").arg(Settings::getBaudrate()));
    ui->comboData->setCurrentIndex(Settings::getDatabits());
    ui->comboStop->setCurrentIndex(Settings::getStopbits());
    ui->comboParity->setCurrentIndex(Settings::getParity());
    ui->hideRecvAreaCheckBox->setChecked(Settings::hideRecvArea());
    ui->hideRecvDataCheckBox->setChecked(Settings::hideRecvData());
    ui->dataFormatCheckBox->setChecked(Settings::displayRawData());
    lineColorNamesStrList = Settings::getLineNames();

    if(ui->hideRecvAreaCheckBox->isChecked())
    {
        ui->textBrowser->setVisible(false);
    }

    QList<int> l = Settings::plotArgs();
    ui->spinPoints->setValue(l[0]);
    ui->spinYStep->setValue(l[1]);
    ui->spinAxesMin->setValue(l[2]);
    ui->spinAxesMax->setValue(l[3]);

    enable_com_controls(true);

    resize(Settings::windowSize());
    move(Settings::windowPos());
}

void MainWindow::setupPlot()
{
    foreach(QString color, Settings::getLineColors())
    {
        lineColorsList.append(QColor(color));
    }

    if(Settings::getTheme() == 0)
    {
        gui_colors[0] = QColor (255,  255,  255,  255); /**<  0: qdark ui dark/background color */
        gui_colors[1] = QColor (175,  175,  175,  255); /**<  0: qdark ui dark/background color */
        gui_colors[2] = QColor (85,   85,   85,   255); /**<  0: qdark ui dark/background color */
        gui_colors[3] = QColor (207,  208,  208,  200); /**<  0: qdark ui dark/background color */

    }else
    {
        gui_colors[0] = QColor (48,  47,  47,  255); /**<  0: qdark ui dark/background color */
        gui_colors[1] = QColor (80,  80,  80,  255); /**<  1: qdark ui medium/grid color */
        gui_colors[2] = QColor (170, 170, 170, 255); /**<  2: qdark ui light/text color */
        gui_colors[3] = QColor (48,  47,  47,  200); /**<  3: qdark ui dark/background color w/transparency */
    }

    /* Background for the plot area */
    ui->plot->setBackground (gui_colors[0]);

    /* Used for higher performance (see QCustomPlot real time example) */
    ui->plot->setNotAntialiasedElements (QCP::aeAll);
    QFont font;
    font.setStyleStrategy (QFont::NoAntialias);
    ui->plot->legend->setFont (font);

    /** See QCustomPlot examples / styled demo **/
    /* X Axis: Style */
    ui->plot->xAxis->grid()->setPen (QPen(gui_colors[2], 1, Qt::DotLine));
    ui->plot->xAxis->grid()->setSubGridPen (QPen(gui_colors[1], 1, Qt::DotLine));
    ui->plot->xAxis->grid()->setSubGridVisible (true);
    ui->plot->xAxis->setBasePen (QPen (gui_colors[2]));
    ui->plot->xAxis->setTickPen (QPen (gui_colors[2]));
    ui->plot->xAxis->setSubTickPen (QPen (gui_colors[2]));
    ui->plot->xAxis->setUpperEnding (QCPLineEnding::esSpikeArrow);
    ui->plot->xAxis->setTickLabelColor (gui_colors[2]);
    ui->plot->xAxis->setTickLabelFont (font);
    /* Range */
    ui->plot->xAxis->setRange (dataPointNumber - ui->spinPoints->value(), dataPointNumber);

    /* Y Axis */
    ui->plot->yAxis->grid()->setPen (QPen(gui_colors[2], 1, Qt::DotLine));
    ui->plot->yAxis->grid()->setSubGridPen (QPen(gui_colors[1], 1, Qt::DotLine));
    ui->plot->yAxis->grid()->setSubGridVisible (true);
    ui->plot->yAxis->setBasePen (QPen (gui_colors[2]));
    ui->plot->yAxis->setTickPen (QPen (gui_colors[2]));
    ui->plot->yAxis->setSubTickPen (QPen (gui_colors[2]));
    ui->plot->yAxis->setUpperEnding (QCPLineEnding::esSpikeArrow);
    ui->plot->yAxis->setTickLabelColor (gui_colors[2]);
    ui->plot->yAxis->setTickLabelFont (font);
    /* Range */
    ui->plot->yAxis->setRange (ui->spinAxesMin->value(), ui->spinAxesMax->value());

    /* User can change Y axis tick step with a spin box */
    //ui->plot->yAxis->setAutoTickStep (false);
    ui->plot->yAxis->ticker()->setTickCount(ui->spinYStep->value());

    /* User interactions Drag and Zoom are allowed only on X axis, Y is fixed manually by UI control */
    ui->plot->setInteraction (QCP::iRangeDrag, true);
    //ui->plot->setInteraction (QCP::iRangeZoom, true);
    ui->plot->setInteraction (QCP::iSelectPlottables, true);
    ui->plot->setInteraction (QCP::iSelectLegend, true);
    ui->plot->axisRect()->setRangeDrag (Qt::Horizontal);
    ui->plot->axisRect()->setRangeZoom (Qt::Horizontal);

    /* Legend */
    QFont legendFont;
    legendFont.setPointSize(9);
    ui->plot->legend->setVisible (true);
    ui->plot->legend->setFont (legendFont);
    ui->plot->legend->setBrush (gui_colors[3]);
    ui->plot->legend->setBorderPen (gui_colors[2]);
    /* By default, the legend is in the inset layout of the main axis rect. So this is how we access it to change legend placement */
    ui->plot->axisRect()->insetLayout()->setInsetAlignment (0, Qt::AlignTop|Qt::AlignRight);

    ui->plot->replot();

    connect (ui->plot, SIGNAL(mouseWheel (QWheelEvent*)), this, SLOT (onMouseWheelInPlot(QWheelEvent*)));
    connect (ui->plot, SIGNAL(mouseMove (QMouseEvent*)), this, SLOT (onMouseMoveInPlot (QMouseEvent*)));
    connect (ui->plot, SIGNAL(selectionChangedByUser()), this, SLOT(channel_selection()));
    //connect (ui->plot, SIGNAL(legendDoubleClick (QCPLegend*, QCPAbstractLegendItem*, QMouseEvent*)), this, SLOT(legend_double_click (QCPLegend*, QCPAbstractLegendItem*, QMouseEvent*)));
    connect (&updateTimer, SIGNAL (timeout()), this, SLOT (replot()));
}

void MainWindow::enable_com_controls (bool enable)
{
    ui->comboBaud->setEnabled (enable);
    ui->comboData->setEnabled (enable);
    ui->comboParity->setEnabled (enable);
    ui->comboPort->setEnabled (enable);
    ui->comboStop->setEnabled (enable);

    ui->actionConnect->setEnabled (enable);
    ui->actionPause_Plot->setEnabled (!enable);
    ui->actionDisconnect->setEnabled (!enable);
//    ui->actionRecord_stream->setEnabled(enable);

    ui->actionHow_to_use->setEnabled(enable);
    ui->actionSettings->setEnabled(enable);
}

void MainWindow::on_comboPort_currentIndexChanged (const QString &arg1)
{
    QSerialPortInfo selectedPort (arg1);                                                   // Dislplay info for selected port
    ui->statusBar->showMessage (selectedPort.description());
}

void MainWindow::replot()
{
    ui->plot->xAxis->setRange (dataPointNumber - ui->spinPoints->value(), dataPointNumber);
    ui->plot->replot();
}

void MainWindow::onNewDataArrived(QStringList newData)
{
    static int data_members = 0;
    static int channel = 0;
    static int i = 0;
    volatile bool you_shall_NOT_PASS = false;

    /* When a fast baud rate is set (921kbps was the first to starts to bug),
       this method is called multiple times (2x in the 921k tests), so a flag
       is used to throttle
       TO-DO: Separate processes, buffer data (1) and process data (2) */
    while (you_shall_NOT_PASS) {}
    you_shall_NOT_PASS = true;

    if (plotting)
    {
        /* Get size of received list */
        data_members = newData.size();

        /* Parse data */
        for (i = 0; i < data_members; i++)
        {
            /* Update number of axes if needed */
            while (ui->plot->plottableCount() <= channel)
            {
                /* Add new channel data */
                ui->plot->addGraph();
                ui->plot->graph()->setPen (lineColorsList[channels % lineColorsList.size()]);
                ui->plot->graph()->setName (lineColorNamesStrList[channels % lineColorNamesStrList.size()]);
                if(ui->plot->legend->item(channels))
                {
                    ui->plot->legend->item (channels)->setTextColor (lineColorsList[channels % lineColorsList.size()]);
                }
                ui->listWidget_Channels->addItem(ui->plot->graph()->name());
                ui->listWidget_Channels->item(channel)->setForeground(QBrush(lineColorsList[channels % lineColorsList.size()]));
                channels++;
            }

            /* [TODO] Method selection and plotting */
            /* X-Y */
            if (0)
            {

            }
            /* Rolling (v1.0.0 compatible) */
            else
            {
                /* Add data to Graph 0 */
                ui->plot->graph(channel)->addData (dataPointNumber, newData[channel].toDouble());
                /* Increment data number and channel */
                channel++;
            }
        }

        /* Post-parsing */
        /* X-Y */
        if (0)
        {

        }
        /* Rolling (v1.0.0 compatible) */
        else
        {
            dataPointNumber++;
            channel = 0;
        }
    }
    you_shall_NOT_PASS = false;
}

void MainWindow::on_spinPoints_valueChanged (int arg1)
{
    Q_UNUSED(arg1)
    int step = (float)abs(ui->spinPoints->value()) * 0.025f;
    if(step == 0)
    {
        step = 1;
    }
    ui->spinPoints->setSingleStep(step);
    ui->plot->xAxis->setRange (dataPointNumber - ui->spinPoints->value(), dataPointNumber);
    ui->plot->replot();
}

void MainWindow::on_spinAxesMin_valueChanged(int arg1)
{
    int step = (float)abs(ui->spinAxesMin->value()) * 0.025f;
    if(step == 0)
    {
        step = 1;
    }
    ui->spinAxesMin->setSingleStep(step);
    ui->plot->yAxis->setRangeLower (arg1);
    ui->plot->replot();
}

void MainWindow::on_spinAxesMax_valueChanged(int arg1)
{
    int step = (float)abs(ui->spinAxesMax->value()) * 0.025f;
    if(step == 0)
    {
        step = 1;
    }
    ui->spinAxesMax->setSingleStep(step);
    ui->plot->yAxis->setRangeUpper (arg1);
    ui->plot->replot();
}

void MainWindow::readData()
{
    if(serialPort->bytesAvailable()) {                                                    // If any bytes are available
        QByteArray data = serialPort->readAll();                                          // Read all data in QByteArray

        if(!data.isEmpty()) {                                                             // If the byte array is not empty
            char *temp = data.data();                                                     // Get a '\0'-terminated char* to the data

            if(!ui->hideRecvDataCheckBox->isChecked() && ui->dataFormatCheckBox->isChecked())
            {
                ui->textBrowser->insertPlainText(QString(data));
            }

            for(int i = 0; i != data.size(); i++) {                                       // Iterate over the char*
                switch(STATE) {                                                           // Switch the current state of the message
                case WAIT_START:                                                          // If waiting for start [$], examine each char
                    if(temp[i] == START_MSG) {                                            // If the char is $, change STATE to IN_MESSAGE
                        STATE = IN_MESSAGE;
                        receivedData.clear();                                             // Clear temporary QString that holds the message
                        break;                                                            // Break out of the switch
                    }
                    break;
                case IN_MESSAGE:                                                          // If state is IN_MESSAGE
                    if(temp[i] == END_MSG) {                                              // If char examined is ;, switch state to END_MSG
                        STATE = WAIT_START;
                        QStringList incomingData = receivedData.split(' ');               // Split string received from port and put it into list
                        if(!ui->hideRecvDataCheckBox->isChecked() && !ui->dataFormatCheckBox->isChecked())
                        {
                            ui->textBrowser->append(receivedData);
                        }
                        emit newData(incomingData);                                       // Emit signal for data received with the list
                        receivedData.clear();
                        break;
                    }
                    else if (isdigit (temp[i]) || isspace (temp[i]) || temp[i] =='-' || temp[i] =='.')
                    {
                        /* If examined char is a digit, and not '$' or ';', append it to temporary string */
                        receivedData.append(temp[i]);
                    }
                    break;
                default: break;
                }
            }
        }
    }
}

void MainWindow::on_spinYStep_valueChanged(int arg1)
{
    ui->plot->yAxis->ticker()->setTickCount(arg1);
    ui->plot->replot();
    ui->spinYStep->setValue(ui->plot->yAxis->ticker()->tickCount());
}

void MainWindow::on_savePNGButton_clicked()
{
    ui->plot->savePng(QDateTime::currentDateTime().toString("yyyy-MM-d-HH-mm-ss-") + QString("snapshot.png"), 1920, 1080, 2, 50);
}

void MainWindow::onMouseMoveInPlot(QMouseEvent *event)
{
    int xx = int(ui->plot->xAxis->pixelToCoord(event->x()));
    int yy = int(ui->plot->yAxis->pixelToCoord(event->y()));
    QString coordinates("X: %1 Y: %2");
    coordinates = coordinates.arg(xx).arg(yy);
    ui->statusBar->showMessage(coordinates);
}

void MainWindow::onMouseWheelInPlot(QWheelEvent *event)
{
    QWheelEvent inverted_event = QWheelEvent(event->posF(), event->globalPosF(),
                                             -event->pixelDelta(), -event->angleDelta(),
                                             0, Qt::Vertical, event->buttons(), event->modifiers());
    QApplication::sendEvent (ui->spinPoints, &inverted_event);
}

void MainWindow::channel_selection (void)
{
    /* synchronize selection of graphs with selection of corresponding legend items */
    for (int i = 0; i < ui->plot->graphCount(); i++)
    {
        QCPGraph *graph = ui->plot->graph(i);
        QCPPlottableLegendItem *item = ui->plot->legend->itemWithPlottable (graph);
        if (item->selected())
        {
            item->setSelected (true);
            //          graph->set (true);
        }
        else
        {
            item->setSelected (false);
            //        graph->setSelected (false);
        }
    }
}

void MainWindow::legend_double_click(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
{
    Q_UNUSED (legend)
    Q_UNUSED(event)
    /* Only react if item was clicked (user could have clicked on border padding of legend where there is no item, then item is 0) */
    if (item)
    {
        QCPPlottableLegendItem *plItem = qobject_cast<QCPPlottableLegendItem*>(item);
        bool ok;
        QString newName = QInputDialog::getText (this, tr("设置通道名称"), tr("名称:"), QLineEdit::Normal, plItem->plottable()->name(), &ok, Qt::FramelessWindowHint);
        if (ok)
        {
            plItem->plottable()->setName(newName);
            for(int i=0; i<ui->plot->graphCount(); i++)
            {
                ui->listWidget_Channels->item(i)->setText(ui->plot->graph(i)->name());
            }
            ui->plot->replot();
        }
    }
}

void MainWindow::on_actionHow_to_use_triggered()
{
    HelpWindow *helpWindow = new HelpWindow (nullptr);
    helpWindow->setAttribute(Qt::WA_DeleteOnClose);
    helpWindow->setModal(true);
    helpWindow->show();
}

void MainWindow::on_actionConnect_triggered()
{
    if (connected)
    {
        /* Is connected, restart if paused */
        if (!plotting)
        {
            plotting = true;
            updateTimer.start(10);
            ui->actionConnect->setEnabled (false);
            ui->actionPause_Plot->setEnabled (true);
            ui->statusBar->showMessage (tr("开始绘制!"));
        }
    }
    else
    {
        /* If application is not connected, connect */
        /* Get parameters from controls first */
        QSerialPortInfo portInfo (ui->comboPort->currentText());                          // Temporary object, needed to create QSerialPort
        int baudRate = ui->comboBaud->currentText().toInt();                              // Get baud rate from combo box
        int dataBitsIndex = ui->comboData->currentIndex();                                // Get index of data bits combo box
        int parityIndex = ui->comboParity->currentIndex();                                // Get index of parity combo box
        int stopBitsIndex = ui->comboStop->currentIndex();                                // Get index of stop bits combo box
        QSerialPort::DataBits dataBits;
        QSerialPort::Parity parity;
        QSerialPort::StopBits stopBits;

        /* Set data bits according to the selected index */
        switch (dataBitsIndex)
        {
        case 0:
            dataBits = QSerialPort::Data8;
            break;
        default:
            dataBits = QSerialPort::Data7;
        }

        /* Set parity according to the selected index */
        switch (parityIndex)
        {
        case 0:
            parity = QSerialPort::NoParity;
            break;
        case 1:
            parity = QSerialPort::OddParity;
            break;
        default:
            parity = QSerialPort::EvenParity;
        }

        /* Set stop bits according to the selected index */
        switch (stopBitsIndex)
        {
        case 0:
            stopBits = QSerialPort::OneStop;
            break;
        default:
            stopBits = QSerialPort::TwoStop;
        }

        serialPort->close();
        serialPort->setPort(portInfo);
        serialPort->setBaudRate (baudRate);
        serialPort->setParity (parity);
        serialPort->setDataBits (dataBits);
        serialPort->setStopBits (stopBits);

        if (serialPort->open(QIODevice::ReadWrite))
        {
            serialPort->clear();
            serialPort->clearError();

            enable_com_controls (false);                                                                // Disable controls if port is open

            updateTimer.start(10);                                                                 // Slot is refreshed 20 times per second
            connected = true;                                                                      // Set flags
            plotting = true;

            ui->statusBar->showMessage (tr("端口打开成功!"));
        }
        else
        {
            ui->statusBar->showMessage (tr("打开失败，请检查该端口是否存在或被占用!"));
        }
    }
}

void MainWindow::on_actionPause_Plot_triggered()
{
    if (plotting)
    {
        updateTimer.stop();                                                               // Stop updating plot timer
        plotting = false;
        ui->actionConnect->setEnabled (true);
        ui->actionPause_Plot->setEnabled (false);
        ui->statusBar->showMessage (tr("暂停绘制，新数据将被忽略！"));
    }
}

void MainWindow::on_actionRecord_stream_triggered()
{
    if(ui->actionRecord_stream->isChecked())
    {
        openCsvFile();
    }else
    {
        closeCsvFile();
    }
}

void MainWindow::on_actionDisconnect_triggered()
{
    if (connected)
    {
        serialPort->close();                                                              // Close serial port
        updateTimer.stop();
        receivedData.clear();                                                             // Clear received string
        connected = false;
        plotting = false;
        enable_com_controls (true);
        ui->statusBar->showMessage (tr("关闭端口!"));
    }
}

void MainWindow::on_actionClear_triggered()
{
    if(serialPort->isOpen())
    {
        serialPort->clear();
    }
    receivedData.clear();
    ui->textBrowser->clear();
    ui->listWidget_Channels->clear();
    channels = 0;
    dataPointNumber = 0;
    ui->plot->clearPlottables();
    ui->plot->replot();
}

void MainWindow::openCsvFile(void)
{
    QString path = Settings::lastSavePath();
    QString filename = QFileDialog::getSaveFileName(this, QApplication::applicationName(), path, tr("xlsx (*.xlsx)"));
    if(filename.isEmpty())
    {
        ui->actionRecord_stream->setChecked(false);
        return;
    }
    QDir dir;
    if(dir.exists(filename))
        dir.remove(filename);
    QFileInfo finfo = QFileInfo(filename);
    xlsxDevice = new QXlsx::Document(filename);
    if(!xlsxDevice)
    {
        ui->actionRecord_stream->setChecked(false);
        QMessageBox::critical(this, QApplication::applicationName(), tr("错误：创建%1.%2失败，请重试！").arg(finfo.baseName()).arg(finfo.completeSuffix()), QMessageBox::Close);
        return;
    }
    xlsxRow = 1;
    xlsxDevice->write(xlsxRow, 1, tr("时间"));
    QStringList titles = Settings::getLineNames();
    for(int i = 0; i < titles.size(); i++)
    {
        xlsxDevice->write(xlsxRow, i + 2, titles[i]);
    }
    xlsxRow++;
    xlsxDevice->save();
    Settings::setLastSavePath(finfo.absolutePath());
    ui->statusBar->showMessage (tr("保存数据到文件！"));
}

void MainWindow::closeCsvFile(void)
{
    if(xlsxDevice)
    {
        xlsxRow = 1;
        xlsxDevice->save();
        delete xlsxDevice;
        xlsxDevice = nullptr;
        ui->statusBar->showMessage (tr("取消保存数据到文件！"));
    }
}

void MainWindow::saveStream(QStringList newData)
{
    if(xlsxDevice && ui->actionRecord_stream->isChecked())
    {
        xlsxDevice->write(xlsxRow, 1, QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss"));
        for(int i = 0; i < newData.size(); i++)
        {
            xlsxDevice->write(xlsxRow, i + 2, newData[i]);
        }
        xlsxRow++;
        xlsxDevice->save();
    }
}

void MainWindow::on_pushButton_AutoScale_clicked()
{
    ui->plot->yAxis->rescale(true);
    ui->spinAxesMax->setValue(int(ui->plot->yAxis->range().upper) + int(ui->plot->yAxis->range().upper*0.1));
    ui->spinAxesMin->setValue(int(ui->plot->yAxis->range().lower) + int(ui->plot->yAxis->range().lower*0.1));
}

void MainWindow::on_pushButton_ResetVisible_clicked()
{
    for(int i=0; i<ui->plot->graphCount(); i++)
    {
        ui->plot->graph(i)->setVisible(true);
        ui->listWidget_Channels->item(i)->setBackground(Qt::NoBrush);
    }
}

void MainWindow::on_listWidget_Channels_itemDoubleClicked(QListWidgetItem *item)
{
    int graphIdx = ui->listWidget_Channels->currentRow();

    if(ui->plot->graph(graphIdx)->visible())
    {
        ui->plot->graph(graphIdx)->setVisible(false);
        QBrush brush = item->background();
        if(brush.style() == Qt::NoBrush)
        {
            brush = ui->listWidget_Channels->palette().window();
        }
        QColor c = brush.color();
        QColor cset = QColor(255 - c.red(), 255 - c.green(), 255 - c.blue(), c.alpha());
        item->setBackground(QBrush(cset));
    }
    else
    {
        ui->plot->graph(graphIdx)->setVisible(true);
        item->setBackground(Qt::NoBrush);
    }
    ui->plot->replot();
}

void MainWindow::on_pushButton_ClearRecv_clicked()
{
    ui->textBrowser->clear();
}

void MainWindow::on_actionSettings_triggered()
{
    SettingsDialog* d = new SettingsDialog(nullptr);

    // 退出设置自动重启软件
    connect(d, &QDialog::accepted, this, [=](){
        qApp->closeAllWindows(); //或qApp->quit(),但使用quit不会调用closeEvent
        QProcess::startDetached(qApp->applicationFilePath(), QStringList());
    });

    d->setWindowTitle(tr("设置"));
    d->setAttribute(Qt::WA_DeleteOnClose);
    d->setModal(true);   //  阻塞父窗口
    d->show();
}

bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
    if(event->type() == QEvent::MouseButtonPress)
    {
        if(watched == ui->comboPort)
        {
            QComboBox* comboBox = qobject_cast<QComboBox *>(watched);
            comboBox->clear();
            foreach (QSerialPortInfo info, QSerialPortInfo::availablePorts())
            {
                comboBox->addItem(info.portName());
            }
        }
    }
    return QMainWindow::eventFilter(watched, event);
}
